﻿# -*- coding: utf-8 -*-

import sys
import os
from selenium.webdriver.support.wait import WebDriverWait
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
import time
from datetime import datetime
import SendMsg
from multiprocessing import Process, freeze_support
import json
import traceback
from PrintLogger import setup_print_log
from SettingLoader import Setting

# print 重定向
setup_print_log()

# 加载全局配置
global_setting = Setting().setting
global_user_name = global_setting['global_user_name']
global_passward = global_setting['global_passward']
global_msg_receive_phone_number = global_setting['global_msg_receive_phone_number']
binary_location = global_setting['binary_location']
executable_path = global_setting['executable_path']
# 配置设置
# ----------------------------------------------

# 设置自动抓取的参数
global_air_conditioning_type = '挂机'  # 可选:挂机 \ 柜机
# 可选： 挂机：小1匹 \ 大1匹 \ 小1.5匹 \ 正1.5匹 \ 2匹 \ 3匹 \ 小2匹 如果为空，将不做筛选
#       柜机：2匹 \ 3匹 \ 4匹 \ 5匹 \ 7匹 \ 大3匹 如果为空，将不做筛选
global_horse_power = '正1.5匹'
global_alarm_price = 2200  # 报警价格，如果价格小于等于alarm_price 将会发短信进行报警
global_is_auto_run_lessprice = False # 自动运行单独抢货脚本
# 记录已经找到的价格和商品，避免重复提醒，key：商品名，value：[价格，查找时间]



# 下面是本程序独有全局变量，外部不改动
global_filter_price_dict = {}
global_loop_count = 0

def get_input_params():
    global global_air_conditioning_type
    global global_horse_power
    global global_alarm_price
    global global_is_auto_run_lessprice
    if len(sys.argv) <= 1:
        return
    for i in range(1, len(sys.argv)):
        inputarg = sys.argv[i]
        indexeq = inputarg.index('=')
        if indexeq <= 0:
            continue
        paramstr = inputarg[0:indexeq].strip()
        val = inputarg[indexeq+1:].strip()
        print(paramstr, val)
        if paramstr == 'global_air_conditioning_type':
            global_air_conditioning_type = val
        elif paramstr == 'global_horse_power':
            global_horse_power = val
        elif paramstr == 'global_alarm_price':
            global_alarm_price = int(val)
        elif paramstr == 'global_is_auto_run_lessprice':
            global_is_auto_run_lessprice = bool(val)



# ===========================================================================================

class JsonToDatetime(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, datetime):
            return obj.strftime('%Y-%m-%d %H:%M:%S')
        elif isinstance(obj, date):
            return obj.strftime('%Y-%m-%d')
        else:
            return json.JSONEncoder.default(self, obj)


def make_filter_dict_file_name():
    return f'自动刷新_{global_air_conditioning_type}_{global_horse_power}_{global_alarm_price}.json'


def save_filter_price_dict():
    global global_filter_price_dict
    filename = make_filter_dict_file_name()
    with open(filename, 'w') as file_obj:
        json.dump(global_filter_price_dict, file_obj, cls=JsonToDatetime)


def load_filter_price_dict():
    global global_filter_price_dict
    filename = make_filter_dict_file_name()
    try:
        with open(filename, 'r') as file_obj:
            global_filter_price_dict = json.load(file_obj)
            # 把时间转换为datetime
            for k, v in global_filter_price_dict.items():
                v[1] = datetime.strptime(v[1], '%Y-%m-%d %H:%M:%S')
            print(f'成功加载filter_price_dict：{global_filter_price_dict}')
    except Exception as e:
        print(e)


def start_browser(window_size=(375, 980)):
    '''
    启动浏览器，返回browser
    '''
    try:
        chrome_options = Options()
        chrome_driver = chrome_options.binary_location
        browser = webdriver.Chrome()
    except Exception as e:
        # 无法自动获取，就需要手动设置
        chrome_options = Options()
        print('=============================================================')
        print(f'需要手动设置谷歌浏览器的执行文件位置和谷歌驱动位置')
        print('=============================================================')
        chrome_options.binary_location = binary_location
        browser = webdriver.Chrome(executable_path=executable_path, options=chrome_options)
    # 把屏幕设置为和手机相当大小
    browser.set_window_size(window_size[0], window_size[1])
    return browser


def browser_login(browser, user, passward):
    '''
    登陆
    param:
     user 用户名
     passward 密码
    '''
    browser.get("https://fmall.gree.com/mobile/index.html#/login")
    WebDriverWait(browser, 10).until(
        lambda browser: browser.find_element_by_xpath('//*[@id="app"]/div[1]/div/div[1]/div[2]/div[2]')).click()
    WebDriverWait(browser, 10).until(
        lambda browser: browser.find_element_by_xpath(
            '//*[@id="app"]/div[1]/div/div[1]/div[3]/div/div[1]/div/input')).send_keys(user)
    WebDriverWait(browser, 10).until(
        lambda browser: browser.find_element_by_xpath(
            '//*[@id="app"]/div[1]/div/div[1]/div[3]/div/div[2]/div/input')).send_keys(passward)
    WebDriverWait(browser, 10).until(
        lambda browser: browser.find_element_by_xpath('//*[@id="app"]/div[1]/div/div[1]/div[3]/div/p[1]')).click()


def browser_close_popup(browser):
    '''
    关闭弹窗
    '''
    try:
        btn_div = WebDriverWait(browser, 10).until(
            lambda browser: browser.find_element_by_css_selector(
                "[class='close_Popup']"))
        browser.execute_script("arguments[0].click();", btn_div)
    except Exception as e:
        print(e)

    element1 = browser.find_element_by_xpath('//*[@id="app"]/div[1]/div[1]/div[3]/div[1]/div/ul/li[5]/span')
    browser.execute_script("arguments[0].click();", element1)


def browser_filter_goods_list(browser):
    '''
    进入商品列表中,并进行筛选
    '''
    # 筛选
    btn_div = WebDriverWait(browser, 10).until(
        lambda browser: browser.find_element_by_xpath('//*[@id="app"]/div[1]/div[2]/div[2]'))
    browser.execute_script("arguments[0].click();", btn_div)
    time.sleep(0.1)

    # 先选中这个弹出的div
    filter_div = WebDriverWait(browser, 10).until(
        lambda browser: browser.find_element_by_xpath('//*[@id="app"]/div[1]/div[5]'))

    # 筛选 - 空调
    filter_div_aircondition = WebDriverWait(browser, 10).until(
        lambda browser: browser.find_element_by_xpath('//*[@id="app"]/div[1]/div[5]/div[1]/div[2]/div[2]'))
    WebDriverWait(browser, 10).until(
        lambda browser: browser.find_element_by_xpath(
            '//*[@id="app"]/div[1]/div[5]/div[1]/div[2]/div[2]/span')).click()
    time.sleep(0.1)

    # 筛选 - 空调 - 家用
    # filter_div = WebDriverWait(browser, 10).until(
    #     lambda browser: browser.find_element_by_xpath('//*[@id="app"]/div[1]/div[5]'))
    filter_div_hometype = WebDriverWait(browser, 10).until(
        lambda browser: browser.find_element_by_xpath(
            '//*[@id="app"]/div[1]/div[5]/div[1]/div[2]/div[2]/div/div[2]'))
    WebDriverWait(browser, 10).until(
        lambda browser: browser.find_element_by_xpath(
            '//*[@id="app"]/div[1]/div[5]/div[1]/div[2]/div[2]/div/div[2]/span')).click()
    time.sleep(0.1)

    # 筛选挂机
    filter_div_hanging_type = WebDriverWait(browser, 10).until(
        lambda browser: browser.find_element_by_xpath(
            '//*[@id="app"]/div[1]/div[5]/div[1]/div[2]/div[2]/div[2]/div[2]'))
    if global_air_conditioning_type == '挂机':
        WebDriverWait(browser, 10).until(
            lambda browser: browser.find_element_by_xpath(
                '//*[@id="app"]/div[1]/div[5]/div[1]/div[2]/div[2]/div[2]/div[2]/span[3]')).click()
    elif global_air_conditioning_type == '柜机':
        WebDriverWait(browser, 10).until(
            lambda browser: browser.find_element_by_xpath(
                '//*[@id="app"]/div[1]/div[5]/div[1]/div[2]/div[2]/div[2]/div[2]/span[2]')).click()
    print(f'根据参数air_conditioning_type进行筛选，当前选择{global_air_conditioning_type}')
    time.sleep(0.1)

    # 筛选匹数
    if global_horse_power is not None:
        if global_air_conditioning_type == '挂机':
            filter_div_horse_power = WebDriverWait(browser, 10).until(
                lambda browser: browser.find_element_by_xpath(
                    '//*[@id="app"]/div[1]/div[5]/div[1]/div[3]/div[3]/div[1]/div[1]'))
            filter_div_horse_power.click()
            time.sleep(0.5)
            WebDriverWait(browser, 10).until(
                lambda browser: browser.find_element_by_xpath(
                    '//*[@id="app"]/div[1]/div[5]/div[1]/div[3]/div[3]/div[2]'))
            if global_horse_power == '小1匹':
                WebDriverWait(browser, 10).until(
                    lambda browser: browser.find_element_by_xpath(
                        '//*[@id="app"]/div[1]/div[5]/div[1]/div[3]/div[3]/div[2]/span[1]')).click()
            elif global_horse_power == '大1匹':
                WebDriverWait(browser, 10).until(
                    lambda browser: browser.find_element_by_xpath(
                        '//*[@id="app"]/div[1]/div[5]/div[1]/div[3]/div[3]/div[2]/span[2]')).click()
            elif global_horse_power == '小1.5匹':
                WebDriverWait(browser, 10).until(
                    lambda browser: browser.find_element_by_xpath(
                        '//*[@id="app"]/div[1]/div[5]/div[1]/div[3]/div[3]/div[2]/span[3]')).click()
            elif global_horse_power == '正1.5匹':
                WebDriverWait(browser, 10).until(
                    lambda browser: browser.find_element_by_xpath(
                        '//*[@id="app"]/div[1]/div[5]/div[1]/div[3]/div[3]/div[2]/span[4]')).click()
            elif global_horse_power == '2匹':
                WebDriverWait(browser, 10).until(
                    lambda browser: browser.find_element_by_xpath(
                        '//*[@id="app"]/div[1]/div[5]/div[1]/div[3]/div[3]/div[2]/span[5]')).click()
            elif global_horse_power == '3匹':
                WebDriverWait(browser, 10).until(
                    lambda browser: browser.find_element_by_xpath(
                        '//*[@id="app"]/div[1]/div[5]/div[1]/div[3]/div[3]/div[2]/span[6]')).click()
            elif global_horse_power == '小2匹':
                WebDriverWait(browser, 10).until(
                    lambda browser: browser.find_element_by_xpath(
                        '//*[@id="app"]/div[1]/div[5]/div[1]/div[3]/div[3]/div[2]/span[7]')).click()
        else:
            filter_div_horse_power = WebDriverWait(browser, 10).until(
                lambda browser: browser.find_element_by_xpath(
                    '//*[@id="app"]/div[1]/div[5]/div[1]/div[3]/div[3]/div[1]/div[1]'))
            filter_div_horse_power.click()
            time.sleep(0.5)
            WebDriverWait(browser, 10).until(
                lambda browser: browser.find_element_by_xpath(
                    '//*[@id="app"]/div[1]/div[5]/div[1]/div[3]/div[3]/div[2]'))
            if global_horse_power == '2匹':
                WebDriverWait(browser, 10).until(
                    lambda browser: browser.find_element_by_xpath(
                        '//*[@id="app"]/div[1]/div[5]/div[1]/div[3]/div[3]/div[2]/span[1]')).click()
            elif global_horse_power == '3匹':
                WebDriverWait(browser, 10).until(
                    lambda browser: browser.find_element_by_xpath(
                        '//*[@id="app"]/div[1]/div[5]/div[1]/div[3]/div[3]/div[2]/span[2]')).click()
            elif global_horse_power == '4匹':
                WebDriverWait(browser, 10).until(
                    lambda browser: browser.find_element_by_xpath(
                        '//*[@id="app"]/div[1]/div[5]/div[1]/div[3]/div[3]/div[2]/span[3]')).click()
            elif global_horse_power == '5匹':
                WebDriverWait(browser, 10).until(
                    lambda browser: browser.find_element_by_xpath(
                        '//*[@id="app"]/div[1]/div[5]/div[1]/div[3]/div[3]/div[2]/span[4]')).click()
            elif global_horse_power == '7匹':
                WebDriverWait(browser, 10).until(
                    lambda browser: browser.find_element_by_xpath(
                        '//*[@id="app"]/div[1]/div[5]/div[1]/div[3]/div[3]/div[2]/span[5]')).click()
            elif global_horse_power == '大3匹':
                WebDriverWait(browser, 10).until(
                    lambda browser: browser.find_element_by_xpath(
                        '//*[@id="app"]/div[1]/div[5]/div[1]/div[3]/div[3]/div[2]/span[6]')).click()

    # 确定筛选
    WebDriverWait(browser, 10).until(
        lambda browser: browser.find_element_by_xpath('//*[@id="app"]/div[1]/div[5]/div[2]/div[2]')).click()
    print('确定筛选')
    time.sleep(0.5)


def browser_sort_price(browser):
    '''
    在价格列表点击排序，按价格升序进行排序
    '''
    WebDriverWait(browser, 10).until(
        lambda browser: browser.find_element_by_xpath(
            '//*[@id="app"]/div[1]/div[2]/div[1]/div[1]/div[2]')).click()
    time.sleep(1)
    WebDriverWait(browser, 10).until(
        lambda browser: browser.find_element_by_xpath(
            '//*[@id="app"]/div[1]/div[2]/div[1]/div[3]/div/div[2]/div[3]')).click()
    time.sleep(1)


def check_goods(goodinfos):
    '''
    检查商品信息
    '''
    global global_loop_count
    accept_goods = []
    is_need_save = False
    for v in goodinfos:
        price = v[0]
        name = v[1]
        price = price.replace('¥', '')
        price = float(price)
        if price <= global_alarm_price:
            nowdtstr = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
            str = f'[{nowdtstr}]发现价格为{price}的产品，小于预警价格{global_alarm_price}，产品名:{name}，请尽快处理'
            if name not in global_filter_price_dict:
                SendMsg.sendMsg(global_msg_receive_phone_number, str)
                print(f'发送短信到{global_msg_receive_phone_number}内容：{str}')
                accept_goods.append((price, name, v[2]))
                global_filter_price_dict[name] = [price, datetime.now()]
                is_need_save = True
            else:
                # 已经在dict中，查看价格是否是更低，如果更低也提醒
                old_price = global_filter_price_dict[name][0]
                if price < old_price:
                    SendMsg.sendMsg(global_msg_receive_phone_number, str)
                    print(f'发送短信到{global_msg_receive_phone_number}内容：{str}')
                    accept_goods.append((price, name, v[2]))
                    global_filter_price_dict[name] = [price, datetime.now()]
                    is_need_save = True
        else:
            # 价格没有低于预警价格，这时候要判断商品是否在列表中，如果在就要删除掉
            if name in global_filter_price_dict:
                global_filter_price_dict.pop(name)
                is_need_save = True
    # 如果所有产品列表都没有在字典里，说明已经下架，也要剔除
    goods_names = [v[1] for v in goodinfos]
    ks = list(global_filter_price_dict.keys())
    pop_names = []
    for k in ks:
        if k not in goods_names:
            global_filter_price_dict.pop(k)
            pop_names.append(k)
            is_need_save = True
    if is_need_save:
        save_filter_price_dict()
    global_loop_count = global_loop_count + 1
    print(datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
          f',{global_air_conditioning_type}{global_horse_power},{global_loop_count},价格序列:',
          [v[0] for v in goodinfos])
    if len(pop_names) > 0:
        print(f'第,{global_loop_count},次查询,以下商品下架：{pop_names}')
    # 对找到的产品{accept_goods}进行价格排序，返回给上级，这时上级将对返回的产品进行自动购买
    if len(accept_goods) > 0:
        accept_goods.sort(key=lambda v: v[0])
        return accept_goods[0]
    return None


def buy_good(url, scripe_name='开始自动抢购脚本.py'):
    '''
    启动购买脚本进行购买
    '''
    os.system(f'python ./{scripe_name} "{url}"')


if __name__ == '__main__':
    get_input_params()
    print(f'用户名:{global_user_name}')
    print(f'筛选类型:{global_air_conditioning_type}')
    print(f'筛选匹数:{global_horse_power}')
    print(f'设定预警价格:{global_alarm_price}')
    print(f'短信通知:{global_msg_receive_phone_number}')
    print(f'是否自动抢:{global_is_auto_run_lessprice}')
    # 多进程支持
    freeze_support()
    # 计数 这个计数用于重启
    count = 0
    # 加载价格列表
    load_filter_price_dict()

    while True:
        try:
            # 启动浏览器
            browser = start_browser()
            # 登录
            browser_login(browser, global_user_name, global_passward)
            # 关闭弹窗
            browser_close_popup(browser)
            # 筛选
            browser_filter_goods_list(browser)
            # 循环价格排序
            while True:
                count += 1
                if count > 100:
                    count = 0
                    browser.quit()
                    break
                try:
                    # 价格排序
                    try:
                        # 有可能网络繁忙导致价格排序失败，等待2秒后再次排序
                        browser_sort_price(browser)
                    except Exception as e:
                        time.sleep(2)
                        browser_sort_price(browser)
                    # 如果再次失败就会被此层catch到，自动把count设置大于上限重启浏览器
                    goods = browser.find_elements_by_css_selector("[class='list-item_row']")
                    time.sleep(0.5)
                    goods_infos = []
                    for i in goods:
                        price = i.find_element_by_css_selector("[class='item-price']").text  # 价格
                        name = i.find_element_by_css_selector("[class='item-title']").text  # 名字
                        goods_infos.append((price, name, i))
                    info = check_goods(goods_infos)

                    if info:
                        # 拿到商品名称，开始调用抢购脚本
                        price, name, ele = info
                        ele.find_element_by_css_selector("[class='item-title']").click()
                        time.sleep(1)
                        js = 'return window.location.href'
                        url = browser.execute_script(js)
                        print('需要抢购商品的链接', url)
                        WebDriverWait(browser, 10).until(
                            lambda browser: browser.find_element_by_xpath('//*[@id="m_common_header_goback"]')).click()
                        print('返回商品列表页面')
                        if global_is_auto_run_lessprice:
                            p_one = Process(target=buy_good, args=(url,))
                            p_one.start()
                    time.sleep(10)
                except Exception as e:
                    traceback.print_exc()
                    print(f'在刷新过程中出现错误，重启浏览器')
                    count = 101
        except Exception as e:
            traceback.print_exc()
            print(f'重启浏览器过程异常，等等待1分钟')
            time.sleep(60)
